/**
 * VectorTileTool - 一个用于动态加载和管理矢量瓦片（GeoJSON）的 MapTalks 工具类。
 * 它根据地图的缩放级别和视野范围，按需加载 GeoJSON 文件，并渲染为矢量图层。
 *
 * @param {maptalks.Map} map - MapTalks 地图实例。
 * @param {string} urlTemplate - GeoJSON 文件的 URL 模板，例如 'geojson/{z}/{x}/{y}.geojson'。
 * @param {object} [options] - 可选配置项。
 * @param {function} [options.style] - 一个函数，用于为每个几何图形设置样式。
 *                                     参数: (geometry, properties, z, x, y)
 *                                     返回: maptalks.Symbol 或 null。
 * @param {boolean} [options.keepInvisibleTiles=true] - 是否保留视野外的瓦片数据。
 *                                                      true: 保留，适合快速缩放平移，但占用更多内存。
 *                                                      false: 不保留，更省内存，但移出视野再移入会重新加载。
 */
var VectorTileTool = (function () {
  // --- 私有变量和函数，隐藏在闭包中 ---

  /**
   * 防抖函数，用于限制函数的执行频率。
   * 在事件被频繁触发时（如地图移动、缩放），确保函数只在停止触发后的一小段时间内执行一次。
   * @private
   * @param {Function} func - 需要防抖的函数。
   * @param {number} wait - 等待时间（毫秒）。
   * @returns {Function} - 返回一个新的、具有防抖功能的函数。
   */
  function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func.apply(this, args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  /**
   * 根据地图范围、缩放级别和瓦片大小，计算需要加载的瓦片坐标
   * @private
   * @param {maptalks.Extent} extent - 地图范围
   * @param {number} zoom - 缩放级别
   * @param {number} tileSize - 瓦片大小（像素）
   * @returns {Array<{z: number, x: number, y: number}>} 瓦片坐标数组
   */
  function _getTileIndices(extent, zoom, tileSize) {
    const map = this.map; // 假设这个函数是在 VectorTileTool 的方法中被调用的，所以可以访问 this.map
    if (!map) {
      console.error("getTileIndices: 地图实例不可用。");
      return [];
    }

    // 它将经纬度 [lng, lat] 转换为指定缩放级别下的平面坐标（像素）
    const min = map.coordinateToPoint(extent.getMin(), zoom);
    const max = map.coordinateToPoint(extent.getMax(), zoom);

    // Web墨卡托投影下，整个世界在不同缩放级别的瓦片数量: 2 的 zoom 次方
    const worldTileSize = Math.pow(2, zoom);

    // 计算瓦片坐标范围
    // 注意：Y轴方向需要反转，因为地理坐标系的Y轴与瓦片坐标系的Y轴相反
    const minX = Math.floor(min.x / tileSize);
    const maxX = Math.floor(max.x / tileSize);
    const minY = Math.floor(
      (1 - max.y / (256 * worldTileSize)) * worldTileSize
    ); // 反转Y轴
    const maxY = Math.floor(
      (1 - min.y / (256 * worldTileSize)) * worldTileSize
    ); // 反转Y轴

    const tiles = [];
    for (let x = minX; x <= maxX; x++) {
      for (let y = minY; y <= maxY; y++) {
        // 确保瓦片坐标在世界范围内
        if (x >= 0 && x < worldTileSize && y >= 0 && y < worldTileSize) {
          tiles.push({ z: zoom, x: 0, y: 0 });
          // tiles.push({ z: zoom, x: x, y: y });
        }
      }
    }
    return tiles;
  }

  /**
   * 构造函数，不直接暴露
   * @private
   */
  function VectorTileToolConstructor(map, urlTemplate, options) {
    if (!map || !(map instanceof maptalks.Map)) {
      throw new Error("MapTalks map instance is required.");
    }
    if (!urlTemplate || typeof urlTemplate !== "string") {
      throw new Error("URL template string is required.");
    }

    this._map = map;
    this._urlTemplate = urlTemplate;
    this._options = maptalks.Util.extend(
      {
        keepInvisibleTiles: true,
        style: null,
      },
      options
    );

    // 缓存已加载的瓦片，格式: { 'z_x_y': VectorLayer }
    this._loadedTiles = {};
    // 用于存储地图事件监听器的引用，以便移除
    this._mapEventListeners = [];
    // 标记工具是否正在运行
    this._isRunning = false;
  }

  /**
   * 根据地图状态加载所有可见的瓦片
   * @private
   */
  VectorTileToolConstructor.prototype._loadVisibleTiles = function () {
    // console.log('-------_loadVisibleTiles-------'); // 调试用
    const map = this._map;
    const zoom = Math.floor(map.getZoom());
    const extent = map.getExtent();
    const tileSize = 512; // GeoJSON 通常按 512x512 的瓦片大小组织，可根据实际情况调整

    const maxExtent = this._options.maxExtent
      ? new maptalks.Extent(this._options.maxExtent)
      : null;

    // 如果配置了加载范围，并且当前地图视野与该范围没有交集，则不加载任何瓦片
    if (maxExtent && !extent.intersects(maxExtent)) {
      // console.log('-------不在范围内-------'); // 调试用
      return;
    }

    // 如果层级发送改变，则清理它们
    this._unloadInvisibleTiles(zoom);

    // 获取当前视野覆盖的所有瓦片坐标
    const tileCoords = _getTileIndices(extent, zoom, tileSize);

    tileCoords.forEach((coord) => {
      const key = `${coord.z}_${coord.x}_${coord.y}`;
      console.log("-------在范围内-------", key);
      // 如果这个瓦片还没加载过，则加载它
      if (!this._loadedTiles[key]) {
        this._loadTile(coord.z, coord.x, coord.y);
      }
    });
  };

  /**
   * 加载单个 GeoJSON 瓦片
   * @private
   */
  VectorTileToolConstructor.prototype._loadTile = function (z, x, y) {
    const zoom = Math.floor(z);
    const key = `${zoom}_${x}_${y}`;
    const url =
      this._urlTemplate
        .replace("{z}", zoom)
        .replace("{x}", x)
        .replace("{y}", y) + `?t=${Date.now()}`;

    // 创建一个临时的 VectorLayer 来承载这个瓦片的几何图形
    const tileLayer = new maptalks.VectorLayer(key);
    this._map.addLayer(tileLayer);

    // 标记为“正在加载”，防止重复请求
    this._loadedTiles[key] = tileLayer;
    fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
      })
      .then((geojson) => {
        // 将 GeoJSON 转换为几何图形
        const geometries = maptalks.GeoJSON.toGeometry(geojson);

        if (geometries && geometries.length > 0) {
          geometries.forEach((geometry) => {
            // 应用样式
            if (typeof this._options.style === "function") {
              const symbol = this._options.style(
                geometry,
                geometry.getProperties(),
                z,
                x,
                y
              );
              if (symbol) {
                geometry.setSymbol(symbol);
              }
            }
            // 将几何图形添加到对应的瓦片图层
            tileLayer.addGeometry(geometry);
          });
        } else {
          // 如果瓦片是空的，可以选择移除它以节省资源
          this._map.removeLayer(tileLayer);
          delete this._loadedTiles[key];
          console.log(`Tile ${key} is empty, removed.`);
        }
      })
      .catch((error) => {
        //console.error(`Failed to load GeoJSON tile ${url}:`, error);
        // 加载失败，从缓存中移除，并移除图层
        if (this._loadedTiles[key] === tileLayer) {
          delete this._loadedTiles[key];
        }
        this._map.removeLayer(tileLayer);
      });
  };

  /**
   * 卸载瓦片
   * @private
   */
  VectorTileToolConstructor.prototype._unloadInvisibleTiles = function (zoom) {
    // 只有放大层级发送改变时清空
    if (this._lastZoom == zoom) {
      return;
    }
    this._lastZoom = zoom;

    for (const key in this._loadedTiles) {
      const layer = this._loadedTiles[key];
      this._map.removeLayer(layer);
      delete this._loadedTiles[key];
    }
  };

  // --- 公共方法 ---

  /**
   * 启动工具，开始监听地图事件并动态加载瓦片
   */
  VectorTileToolConstructor.prototype.start = function () {
    // 防止重复启动
    if (this._isRunning) return;
    this._isRunning = true;
    this._lastZoom = -1;

    // 使用我们自定义的防抖函数，避免在快速拖动或缩放时频繁触发加载
    const debouncedLoad = debounce(this._loadVisibleTiles.bind(this), 100);

    // 监听地图事件
    this._map.on("moving zooming", debouncedLoad);
    this._mapEventListeners.push(["moving zooming", debouncedLoad]);

    // 初始加载一次
    this._loadVisibleTiles();
  };

  /**
   * 停止工具，移除所有事件监听器
   */
  VectorTileToolConstructor.prototype.stop = function () {
    if (!this._isRunning) return;
    this._isRunning = false;

    this._mapEventListeners.forEach(([event, handler]) => {
      this._map.off(event, handler);
    });
    this._mapEventListeners = [];
  };

  /**
   * 销毁工具，清理所有资源，包括事件监听器和所有创建的图层
   */
  VectorTileToolConstructor.prototype.destroy = function () {
    this.stop(); // 先停止监听

    // 移除所有已加载的瓦片图层
    for (const key in this._loadedTiles) {
      const layer = this._loadedTiles[key];
      if (layer) {
        this._map.removeLayer(layer);
      }
    }
    this._loadedTiles = {};

    // 清除对 map 实例的引用
    this._map = null;
  };

  /**
   * 手动触发一次视野内瓦片的加载
   */
  VectorTileToolConstructor.prototype.refresh = function () {
    if (this._isRunning) {
      this._loadVisibleTiles();
    }
  };

  // --- 暴露公共接口 ---
  return function (map, urlTemplate, options) {
    return new VectorTileToolConstructor(map, urlTemplate, options);
  };
})();
